<!-- build time:Tue Dec 17 2019 19:26:42 GMT+0800 (GMT+08:00) --><!doctype html><html class="theme-next mist" lang="zh-Hans"><head><meta name="generator" content="Hexo 3.8.0"><meta name="google-site-verification" content="7Tau9WyVgxnsEY9oYedu9g0U6_8akOX3wiKbaYcrg9A"><meta name="baidu-site-verification" content="EVwLiaxdxX"><link href="/css/xps13.css" rel="stylesheet" type="text/css"><link href="/css/message.css" rel="stylesheet" type="text/css"><script type="text/javascript" src="/js/jquery-1.11.3.min.js"></script><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1"><meta http-equiv="Cache-Control" content="no-transform"><meta http-equiv="Cache-Control" content="no-siteapp"><link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css"><link href="/css/main.css?v=5.1.1" rel="stylesheet" type="text/css"><meta name="keywords" content="Spring,"><link rel="alternate" href="/atom.xml" title="MrBird" type="application/atom+xml"><link rel="shortcut icon" type="image/x-icon" href="/favicon.ico?v=5.1.1"><meta name="description" content="接触过Spring的同学肯定都听过IOC。在传统的Java编程中，当需要用到某个对象的时候，我们都是主动地显示创建一个对象实例（new）。使用Spring后就不需要这样做了，因为Spring会帮我们在需要用到某些对象的地方自动注入该对象，而无须我们自己去创建。这种模式俗称控制反转，即IOC（Inversion of Control）。那么Spring是从什么地方获取到我们所需要的对象呢？其实Spr"><meta name="keywords" content="Spring"><meta property="og:type" content="article"><meta property="og:title" content="深入学习Spring组件注册"><meta property="og:url" content="http://mrbird.cc/Spring-Bean-Regist.html"><meta property="og:site_name" content="MrBird"><meta property="og:description" content="接触过Spring的同学肯定都听过IOC。在传统的Java编程中，当需要用到某个对象的时候，我们都是主动地显示创建一个对象实例（new）。使用Spring后就不需要这样做了，因为Spring会帮我们在需要用到某些对象的地方自动注入该对象，而无须我们自己去创建。这种模式俗称控制反转，即IOC（Inversion of Control）。那么Spring是从什么地方获取到我们所需要的对象呢？其实Spr"><meta property="og:locale" content="zh-Hans"><meta property="og:image" content="http://mrbird.cc/img/QQ截图20181207155127.png"><meta property="og:image" content="http://mrbird.cc/img/QQ截图20181207155718.png"><meta property="og:image" content="http://mrbird.cc/img/QQ截图20181207164418.png"><meta property="og:image" content="http://mrbird.cc/img/QQ截图20190129103606.png"><meta property="og:image" content="http://mrbird.cc/img/QQ截图20190129100306.png"><meta property="og:image" content="http://mrbird.cc/img/QQ截图20190129100809.png"><meta property="og:image" content="http://mrbird.cc/img/QQ截图20190129101050.png"><meta property="og:image" content="http://mrbird.cc/img/QQ截图20190129112811.png"><meta property="og:image" content="http://mrbird.cc/img/QQ截图20190129135637.png"><meta property="og:image" content="http://mrbird.cc/img/QQ截图20190129140150.png"><meta property="og:image" content="http://mrbird.cc/img/QQ截图20190129144509.png"><meta property="og:image" content="http://mrbird.cc/img/QQ截图20190129144804.png"><meta property="og:image" content="http://mrbird.cc/img/QQ截图20190129145157.png"><meta property="og:image" content="http://mrbird.cc/img/QQ截图20190221094636.png"><meta property="og:image" content="http://mrbird.cc/img/QQ截图20190221094658.png"><meta property="og:image" content="http://mrbird.cc/img/QQ截图20190221094849.png"><meta property="og:image" content="http://mrbird.cc/img/QQ截图20190221094910.png"><meta property="og:image" content="http://mrbird.cc/img/QQ截图20190129185401.png"><meta property="og:image" content="http://mrbird.cc/img/QQ截图20190130091140.png"><meta property="og:image" content="http://mrbird.cc/img/QQ截图20190130094046.png"><meta property="og:image" content="http://mrbird.cc/img/QQ截图20190130094912.png"><meta property="og:image" content="http://mrbird.cc/img/QQ截图20190130103346.png"><meta property="og:image" content="http://mrbird.cc/img/QQ截图20190130103934.png"><meta property="og:image" content="http://mrbird.cc/img/QQ截图20190130104606.png"><meta property="og:image" content="http://mrbird.cc/img/QQ截图20190130104715.png"><meta property="og:updated_time" content="2019-10-28T12:14:46.264Z"><meta name="twitter:card" content="summary"><meta name="twitter:title" content="深入学习Spring组件注册"><meta name="twitter:description" content="接触过Spring的同学肯定都听过IOC。在传统的Java编程中，当需要用到某个对象的时候，我们都是主动地显示创建一个对象实例（new）。使用Spring后就不需要这样做了，因为Spring会帮我们在需要用到某些对象的地方自动注入该对象，而无须我们自己去创建。这种模式俗称控制反转，即IOC（Inversion of Control）。那么Spring是从什么地方获取到我们所需要的对象呢？其实Spr"><meta name="twitter:image" content="http://mrbird.cc/img/QQ截图20181207155127.png"><script type="text/javascript" id="hexo.configurations">var NexT=window.NexT||{},CONFIG={root:"/",scheme:"Mist",sidebar:{position:"left",display:"always",offset:12,offset_float:0,b2t:!1,scrollpercent:!1},fancybox:!1,motion:!1}</script><title>深入学习Spring组件注册 | MrBird</title></head><body ondragstart="return!1" class="animsition" lang="zh-Hans" style="overflow-x:hidden;padding-right:280px"><script type="text/javascript" src="/js/mo.min.js"></script><style>@media (min-width:768px) and (max-width:991px){body .header-innerr{width:700px!important}}.header-innerr{margin:0 auto;padding:100px 0 70px;width:880px}@media (min-width:1600px){.container .header-innerr{width:1200px}}.header-innerr{position:relative}.header-innerr{padding:20px 0 0}.header-innerr:after,.header-innerr:before{content:" ";display:table}.header-innerr:after{clear:both}@media (max-width:767px){.header-innerr{width:auto;padding:10px;margin-bottom:-20px}}</style><div class="container sidebar-position-left page-post-detail"><div class="headband"></div><header id="header" class="header"><div class="header-inner"><div class="site-brand-wrapper"><div class="site-meta"><link href="https://fonts.font.im/css?family=Merienda" rel="stylesheet"><div class="custom-logo-site-title"><a href="/" class="brand" rel="start"><span class="logo-line-before"><i></i></span> <span class="site-title" style="font-family:Merienda;font-size:1.3rem">MrBird</span> <span class="logo-line-after"><i></i></span></a></div><p class="site-subtitle"></p></div><div class="site-nav-toggle"><button><span class="btn-bar"></span> <span class="btn-bar"></span> <span class="btn-bar"></span></button></div></div><nav class="site-nav"><ul id="menu" class="menu"><li class="menu-item menu-item-home"><a href="/" rel="section">HOME</a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section">ARCHIVES</a></li><li class="menu-item menu-item-tags"><a href="/tags/" rel="section">TAGS</a></li><li class="menu-item menu-item-friends"><a href="/friends/" rel="section">FRIENDS</a></li><div class="sidebar-toggle" style="display:none"><div class="sidebar-toggle-line-wrap"><span class="sidebar-toggle-line sidebar-toggle-line-first"></span> <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span> <span class="sidebar-toggle-line sidebar-toggle-line-last"></span></div></div></ul><div class="site-search"><div class="popup search-popup local-search-popup"><div class="local-search-header clearfix"><span class="search-icon"><i class="fa fa-search"></i> </span><span class="popup-btn-close"><i class="fa fa-times-circle"></i></span><div class="local-search-input-wrapper"><input autocomplete="off" placeholder="Search" spellcheck="false" type="text" id="local-search-input"></div></div><div id="local-search-result"></div></div></div></nav></div><div class="header-innerr"><div class="note info" style="margin:0;letter-spacing:.15px">🐤手把手教你搭建<strong>Spring Cloud微服务权限系统</strong>（从零到部署）：<a style="color:#40dab2;font-weight:600" href="https://www.kancloud.cn/mrbird/spring-cloud" target="_blank">https://www.kancloud.cn/mrbird/spring-cloud</a></div></div></header><main id="main" class="main"><div class="main-inner"><div class="content-wrap"><div id="content" class="content"><div id="posts" class="posts-expand"><article class="post post-type-normal" itemscope itemtype="http://schema.org/Article"><link itemprop="mainEntityOfPage" href="http://mrbird.cc/Spring-Bean-Regist.html"><span hidden itemprop="author" itemscope itemtype="http://schema.org/Person"><meta itemprop="name" content="MrBird"><meta itemprop="description" content=""><meta itemprop="image" content="/images/blogImage.jpg"></span><span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization"><meta itemprop="name" content="MrBird"></span><header class="post-header"><h1 class="post-title" itemprop="name headline">深入学习Spring组件注册</h1><div class="post-meta"><span class="post-time"><span class="post-meta-item-icon"><i class="fa fa-calendar-o"></i> </span><span class="post-meta-item-text">Posted on</span> <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-08-01T14:36:09+08:00">2018-08-01 </time></span><span></span> <span class="post-meta-divider">|</span> <span class="page-pv"><i class="fa fa-laptop"></i>&nbsp;&nbsp;Visit count <span class="busuanzi-value" id="busuanzi_value_page_pv"></span></span></div></header><div class="post-body" itemprop="articleBody"><p>接触过Spring的同学肯定都听过IOC。在传统的Java编程中，当需要用到某个对象的时候，我们都是主动地显示创建一个对象实例（new）。使用Spring后就不需要这样做了，因为Spring会帮我们在需要用到某些对象的地方自动注入该对象，而无须我们自己去创建。这种模式俗称控制反转，即IOC（Inversion of Control）。那么Spring是从什么地方获取到我们所需要的对象呢？其实Spring给我们提供了一个IOC容器，里面管理着所有我们需要的对象，组件注册就是我们去告诉Spring哪些类需要交给IOC容器管理。</p><p>这里主要记录组件注册的一些细节。<a id="more"></a></p><h2 id="通过-Bean注册组件"><a href="#通过-Bean注册组件" class="headerlink" title="通过@Bean注册组件"></a>通过@Bean注册组件</h2><p>在较早版本的Spring中，我们都是通过XML的方式来往IOC容器中注册组件的，下面这段代码大家肯定不会陌生：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 返回 IOC 容器，基于 XML配置，传入配置文件的位置</span></span><br><span class="line">ApplicationContext applicationContext = <span class="keyword">new</span> ClassPathXmlApplicationContext(<span class="string">"xxx.xml"</span>);</span><br><span class="line">User user = (User) applicationContext.getBean(<span class="string">"user"</span>);</span><br></pre></td></tr></table></figure><p></p><p>Spring 4后推荐我们使用Java Config的方式来注册组件。</p><p>为了演示，我们通过<a href="http://start.spring.io/" target="_blank" rel="noopener">http://start.spring.io/</a>搭建一个简单Spring Boot应用，然后引入Lombok依赖（编辑器也需要安装Lombok插件）：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.projectlombok<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>lombok<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">scope</span>&gt;</span>provided<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p></p><p>然后创建一个User类：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@ToString</span></span><br><span class="line"><span class="meta">@AllArgsConstructor</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">User</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> Integer age;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>接着创建一个配置类，在里面通过<code>@Bean</code>注解注册User类：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">WebConfig</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Bean</span>()</span><br><span class="line">    <span class="function"><span class="keyword">public</span> User <span class="title">user</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> User(<span class="string">"mrbird"</span>, <span class="number">18</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>通过<code>@Bean</code>注解，我们向IOC容器注册了一个名称为<code>user</code>（Bean名称默认为方法名，我们也可以通过<code>@Bean(&quot;myUser&quot;)</code>方式来将组件名称指定为<code>myUser</code>）。</p><p>组件注册完毕后，我们测试一下从IOC容器中获取这个组件。在Spring Boot入口类中编写如下代码：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DemoApplication</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">    	SpringApplication.run(DemoApplication.class, args);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 返回 IOC 容器，使用注解配置，传入配置类</span></span><br><span class="line">        ApplicationContext context = <span class="keyword">new</span> AnnotationConfigApplicationContext(WebConfig.class);</span><br><span class="line">        User user = context.getBean(User.class);</span><br><span class="line">        System.out.println(user);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>因为我们是通过注解方式来注册组件的，所以需要使用<code>AnnotationConfigApplicationContext</code>来获取相应的IOC容器，入参为配置类。</p><p>启动项目，看下控制台输出：</p><p><img src="img/QQ截图20181207155127.png" alt="QQ截图20181207155127.png"></p><p>说明组件注册成功。</p><p>我们将组件的名称改为<code>myUser</code>，然后看看IOC容器中，User类型组件是否叫<code>myUser</code>：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DemoApplication</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        SpringApplication.run(DemoApplication.class, args);</span><br><span class="line"></span><br><span class="line">        ApplicationContext context = <span class="keyword">new</span> AnnotationConfigApplicationContext(WebConfig.class);</span><br><span class="line">        <span class="comment">// 查看 User 这个类在 Spring 容器中叫啥玩意</span></span><br><span class="line">        String[] beanNames = context.getBeanNamesForType(User.class);</span><br><span class="line">        Arrays.stream(beanNames).forEach(System.out::println);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>启动项目，观察控制台输出:</p><p><img src="img/QQ截图20181207155718.png" alt="QQ截图20181207155718.png"></p><h2 id="使用-ComponentScan扫描"><a href="#使用-ComponentScan扫描" class="headerlink" title="使用@ComponentScan扫描"></a>使用@ComponentScan扫描</h2><p>在使用XML配置组件扫描的时候，我们都是这样配置的：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">context:component-scan</span> <span class="attr">base-package</span>=<span class="string">""</span>&gt;</span><span class="tag">&lt;/<span class="name">context:component-scan</span>&gt;</span></span><br></pre></td></tr></table></figure><p></p><p>其中<code>base-package</code>指定了扫描的路径。路径下所有被<code>@Controller</code>、<code>@Service</code>、<code>@Repository</code>和<code>@Component</code>注解标注的类都会被纳入IOC容器中。</p><p>现在我们脱离XML配置后，可以使用<code>@ComponentScan</code>注解来扫描组件并注册。</p><p>在使用<code>@ComponentScan</code>扫描之前，我们先创建一个Controller，一个Service，一个Dao，并标注上相应的注解。</p><p>然后修改配置类：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan</span>(<span class="string">"cc.mrbird.demo"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">WebConfig</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// @Bean("myUser")</span></span><br><span class="line">    <span class="comment">// public User user() &#123;</span></span><br><span class="line">    <span class="comment">//     return new User("mrbird", 18);</span></span><br><span class="line">    <span class="comment">// &#125;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>在配置类中，我们通过<code>@ComponentScan(&quot;cc.mrbird.demo&quot;)</code>配置了扫描路径，并且将User组件注册注释掉了，取而代之的是在User类上加上<code>@Component</code>注解：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@ToString</span></span><br><span class="line"><span class="meta">@AllArgsConstructor</span></span><br><span class="line"><span class="meta">@NoArgsConstructor</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">User</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> Integer age;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><div class="note danger"><p>值得注意的是，我们不能将Spring Boot的入口类纳入扫描范围中，否则项目启动将出错。</p></div><p>接下来我们看下在基于注解的IOC容器中是否包含了这些组件：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DemoApplication</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        SpringApplication.run(DemoApplication.class, args);</span><br><span class="line"></span><br><span class="line">        ApplicationContext context = <span class="keyword">new</span> AnnotationConfigApplicationContext(WebConfig.class);</span><br><span class="line">        <span class="comment">// 查看基于注解的 IOC容器中所有组件名称</span></span><br><span class="line">        String[] beanNames = context.getBeanDefinitionNames();</span><br><span class="line">        Arrays.stream(beanNames).forEach(System.out::println);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>启动项目，观察控制台：</p><p><img src="img/QQ截图20181207164418.png" alt="QQ截图20181207164418.png"></p><p>可见，组件已经成功被扫描进去了，并且名称默认为类名首字母小写。</p><p>这里，配置类WebConfig也被扫描并注册了，查看<code>@Configuration</code>源码就会发现原因：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Target</span>(ElementType.TYPE)</span><br><span class="line"><span class="meta">@Retention</span>(RetentionPolicy.RUNTIME)</span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> Configuration &#123;</span><br><span class="line">    <span class="function">String <span class="title">value</span><span class="params">()</span> <span class="keyword">default</span> ""</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><h3 id="指定扫描策略"><a href="#指定扫描策略" class="headerlink" title="指定扫描策略"></a>指定扫描策略</h3><p><code>@ComponentScan</code>注解允许我们指定扫描策略，即指定哪些被扫描，哪些不被扫描，查看其源码可发现这两个属性：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Specifies which types are eligible for component scanning.</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;Further narrows the set of candidate components from everything in &#123;<span class="doctag">@link</span> #basePackages&#125;</span></span><br><span class="line"><span class="comment"> * to everything in the base packages that matches the given filter or filters.</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;Note that these filters will be applied in addition to the default filters, if specified.</span></span><br><span class="line"><span class="comment"> * Any type under the specified base packages which matches a given filter will be included,</span></span><br><span class="line"><span class="comment"> * even if it does not match the default filters (i.e. is not annotated with &#123;<span class="doctag">@code</span> <span class="doctag">@Component</span>&#125;).</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> #resourcePattern()</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> #useDefaultFilters()</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">Filter[] includeFilters() <span class="keyword">default</span> &#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Specifies which types are not eligible for component scanning.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> #resourcePattern</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">Filter[] excludeFilters() <span class="keyword">default</span> &#123;&#125;;</span><br></pre></td></tr></table></figure><p></p><p>其中<code>Filter</code>也是一个注解:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Declares the type filter to be used as an &#123;<span class="doctag">@linkplain</span> ComponentScan#includeFilters</span></span><br><span class="line"><span class="comment"> * include filter&#125; or &#123;<span class="doctag">@linkplain</span> ComponentScan#excludeFilters exclude filter&#125;.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Retention</span>(RetentionPolicy.RUNTIME)</span><br><span class="line"><span class="meta">@Target</span>(&#123;&#125;)</span><br><span class="line"><span class="meta">@interface</span> Filter &#123;</span><br><span class="line"></span><br><span class="line">    <span class="function">FilterType <span class="title">type</span><span class="params">()</span> <span class="keyword">default</span> FilterType.ANNOTATION</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@AliasFor</span>(<span class="string">"classes"</span>)</span><br><span class="line">    Class&lt;?&gt;[] value() <span class="keyword">default</span> &#123;&#125;;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@AliasFor</span>(<span class="string">"value"</span>)</span><br><span class="line">    Class&lt;?&gt;[] classes() <span class="keyword">default</span> &#123;&#125;;</span><br><span class="line">    String[] pattern() <span class="keyword">default</span> &#123;&#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>接下来我们使用<code>excludeFilters</code>来排除一些组件的扫描：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan</span>(value = <span class="string">"cc.mrbird.demo"</span>,</span><br><span class="line">        excludeFilters = &#123;</span><br><span class="line">                <span class="meta">@Filter</span>(type = FilterType.ANNOTATION,</span><br><span class="line">                        classes = &#123;Controller.class, Repository.class&#125;),</span><br><span class="line">                <span class="meta">@Filter</span>(type = FilterType.ASSIGNABLE_TYPE, classes = User.class)</span><br><span class="line">        &#125;)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">WebConfig</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>上面我们指定了两种排除扫描的规则：</p><ol><li><p>根据注解来排除（<code>type = FilterType.ANNOTATION</code>）,这些注解的类型为<code>classes = {Controller.class, Repository.class}</code>。即<code>Controller</code>和<code>Repository</code>注解标注的类不再被纳入到IOC容器中。</p></li><li><p>根据指定类型类排除（<code>type = FilterType.ASSIGNABLE_TYPE</code>），排除类型为<code>User.class</code>，其子类，实现类都会被排除。</p></li></ol><p>启动项目，观察控制台：</p><p><img src="img/QQ截图20190129103606.png" alt="QQ截图20190129095439.png"></p><p>可见排除成功。</p><p>除了上面两种常用的规则外，我们还可以使用别的规则，查看<code>FilterType</code>源码：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">enum</span> FilterType &#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Filter candidates marked with a given annotation.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@see</span> org.springframework.core.type.filter.AnnotationTypeFilter</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    ANNOTATION,</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Filter candidates assignable to a given type.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@see</span> org.springframework.core.type.filter.AssignableTypeFilter</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    ASSIGNABLE_TYPE,</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Filter candidates matching a given AspectJ type pattern expression.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@see</span> org.springframework.core.type.filter.AspectJTypeFilter</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    ASPECTJ,</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Filter candidates matching a given regex pattern.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@see</span> org.springframework.core.type.filter.RegexPatternTypeFilter</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    REGEX,</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Filter candidates using a given custom</span></span><br><span class="line"><span class="comment">     * &#123;<span class="doctag">@link</span> org.springframework.core.type.filter.TypeFilter&#125; implementation.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    CUSTOM</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>可看到，我们还可以通过<code>ASPECTJ</code>表达式，<code>REGEX</code>正则表达式和<code>CUSTOM</code>自定义规则（下面详细介绍）来指定扫描策略。</p><p><code>includeFilters</code>的作用和<code>excludeFilters</code>相反，其指定的是哪些组件需要被扫描：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan</span>(value = <span class="string">"cc.mrbird.demo"</span>,</span><br><span class="line">        includeFilters = &#123;</span><br><span class="line">                <span class="meta">@Filter</span>(type = FilterType.ANNOTATION, classes = Service.class)</span><br><span class="line">        &#125;, useDefaultFilters = <span class="keyword">false</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">WebConfig</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>上面配置了只将<code>Controller</code>纳入IOC容器，并且需要用<code>useDefaultFilters = false</code>来关闭Spring默认的扫描策略才能让我们的配置生效（Spring Boot入口类的<code>@SpringBootApplication</code>注解包含了一些默认的扫描策略）。</p><p>启动项目，观察控制台：</p><p><img src="img/QQ截图20190129100306.png" alt="QQ截图20190129100306.png"></p><p>可看到，IOC容器中将不再包含dao，service。</p><h3 id="多扫描策略配置"><a href="#多扫描策略配置" class="headerlink" title="多扫描策略配置"></a>多扫描策略配置</h3><p>在Java 8之前，我们可以使用<code>@ComponentScans</code>来配置多个<code>@ComponentScan</code>以实现多扫描规则配置：</p><p><img src="img/QQ截图20190129100809.png" alt="QQ截图20190129100809.png"></p><p>而在Java 8中，新增了<code>@Repeatable</code>注解，使用该注解修饰的注解可以重复使用，查看<code>@ComponentScan</code>源码会发现其已经被该注解标注：</p><p><img src="img/QQ截图20190129101050.png" alt="QQ截图20190129101050.png"></p><p>所以除了使用<code>@ComponentScans</code>来配置多扫描规则外，我们还可以通过多次使用<code>@ComponentScan</code>来指定多个不同的扫描规则。</p><h3 id="自定义扫描策略"><a href="#自定义扫描策略" class="headerlink" title="自定义扫描策略"></a>自定义扫描策略</h3><p>自定义扫描策略需要我们实现<code>org.springframework.core.type.filter.TypeFilter</code>接口，创建<code>MyTypeFilter</code>实现该接口：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyTypeFilter</span> <span class="keyword">implements</span> <span class="title">TypeFilter</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">match</span><span class="params">(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>该接口包含<code>match</code>方法，其两个入参<code>MetadataReader</code>和<code>MetadataReaderFactory</code>含义如下：</p><ol><li><p><code>MetadataReader</code>：当前正在扫描的类的信息；</p></li><li><p><code>MetadataReaderFactory</code>：可以通过它来获取其他类的信息。</p></li></ol><p>当<code>match</code>方法返回true时说明匹配成功，false则说明匹配失败。继续完善这个过滤规则：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyTypeFilter</span> <span class="keyword">implements</span> <span class="title">TypeFilter</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">match</span><span class="params">(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 获取当前正在扫描的类的注解信息</span></span><br><span class="line">        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();</span><br><span class="line">        <span class="comment">// 获取当前正在扫描的类的类信息</span></span><br><span class="line">        ClassMetadata classMetadata = metadataReader.getClassMetadata();</span><br><span class="line">        <span class="comment">// 获取当前正在扫描的类的路径等信息</span></span><br><span class="line">        Resource resource = metadataReader.getResource();</span><br><span class="line"></span><br><span class="line">        String className = classMetadata.getClassName();</span><br><span class="line">        <span class="keyword">return</span> StringUtils.hasText(<span class="string">"er"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>上面指定了当被扫描的类名包含<code>er</code>时候，匹配成功，配合<code>excludeFilters</code>使用意指当被扫描的类名包含<code>er</code>时，该类不被纳入IOC容器中。</p><p>我们在<code>@ComponentScan</code>中使用这个自定义的过滤策略：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan</span>(value = <span class="string">"cc.mrbird.demo"</span>,</span><br><span class="line">        excludeFilters = &#123;</span><br><span class="line">            <span class="meta">@Filter</span>(type = FilterType.CUSTOM, classes = MyTypeFilter.class)</span><br><span class="line">        &#125;)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">WebConfig</span> </span>&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>启动项目，观察输出： <img src="img/QQ截图20190129112811.png" alt="QQ截图20190129112811.png"></p><p>因为<code>User</code>，<code>UserMapper</code>，<code>UserService</code>和<code>UserController</code>等类的类名都包含<code>er</code>，所以它们都没有被纳入到IOC容器中。</p><h2 id="组件作用域-Scope"><a href="#组件作用域-Scope" class="headerlink" title="组件作用域@Scope"></a>组件作用域@Scope</h2><p>默认情况下，在Spring的IOC容器中每个组件都是单例的，即无论在任何地方注入多少次，这些对象都是同一个，我们来看下例子。</p><p>首先将User对象中的<code>@Component</code>注解去除，然后在配置类中配置User Bean：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">WebConfig</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> User <span class="title">user</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> User(<span class="string">"mrbird"</span>, <span class="number">18</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>接着多次从IOC容器中获取这个组件，看看是否为同一个：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 返回 IOC 容器，使用注解配置，传入配置类</span></span><br><span class="line">ApplicationContext context = <span class="keyword">new</span> AnnotationConfigApplicationContext(WebConfig.class);</span><br><span class="line">Object user1 = context.getBean(<span class="string">"user"</span>);</span><br><span class="line">Object user2 = context.getBean(<span class="string">"user"</span>);</span><br><span class="line">System.out.println(user1 == user2);</span><br></pre></td></tr></table></figure><p></p><p>启动项目，观察控制台输出:</p><p><img src="img/QQ截图20190129135637.png" alt="QQ截图20190129135637.png"></p><p>结果证实了上面的观点。</p><p>在Spring中我们可以使用<code>@Scope</code>注解来改变组件的作用域：</p><p><img src="img/QQ截图20190129140150.png" alt="QQ截图20190129140150.png"></p><ol><li><p><code>singleton</code>：单实例（默认）,在Spring IOC容器启动的时候会调用方法创建对象然后纳入到IOC容器中，以后每次获取都是直接从IOC容器中获取（<code>map.get()</code>）；</p></li><li><p><code>prototype</code>：多实例，IOC容器启动的时候并不会去创建对象，而是在每次获取的时候才会去调用方法创建对象；</p></li><li><p><code>request</code>：一个请求对应一个实例；</p></li><li><p><code>session</code>：同一个session对应一个实例。</p></li></ol><h2 id="懒加载-Lazy"><a href="#懒加载-Lazy" class="headerlink" title="懒加载@Lazy"></a>懒加载@Lazy</h2><p>懒加载是针对单例模式而言的，正如前面所说，IOC容器中的组件默认是单例的，容器启动的时候会调用方法创建对象然后纳入到IOC容器中。</p><p>在User Bean注册的地方加入一句话以观察：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">WebConfig</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> User <span class="title">user</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"往IOC容器中注册user bean"</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> User(<span class="string">"mrbird"</span>, <span class="number">18</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>测试：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">ApplicationContext context = <span class="keyword">new</span> AnnotationConfigApplicationContext(WebConfig.class);</span><br><span class="line">System.out.println(<span class="string">"容器创建完毕"</span>);</span><br></pre></td></tr></table></figure><p></p><p>启动项目观察控制台输出:</p><p><img src="img/QQ截图20190129144509.png" alt="QQ截图20190129144509.png"></p><p>可以看到，在IOC容器创建完毕之前，组件已经添加到容器中了。</p><p>将User Bean改为懒加载的方式：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">WebConfig</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="meta">@Lazy</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> User <span class="title">user</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"往IOC容器中注册user bean"</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> User(<span class="string">"mrbird"</span>, <span class="number">18</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>再次启动项目，观察输出：</p><p><img src="img/QQ截图20190129144804.png" alt="QQ截图20190129144804.png"></p><p>可看到，容器创建完的时候，User Bean这个组件并未添加到容器中。</p><div class="note info"><p>所以懒加载的功能是，在单例模式中，IOC容器创建的时候不会马上去调用方法创建对象并注册，只有当组件<strong>第一次</strong>被使用的时候才会调用方法创建对象并加入到容器中。</p></div><p>测试一下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">ApplicationContext context = <span class="keyword">new</span> AnnotationConfigApplicationContext(WebConfig.class);</span><br><span class="line">System.out.println(<span class="string">"容器创建完毕"</span>);</span><br><span class="line">Object user1 = context.getBean(<span class="string">"user"</span>);</span><br><span class="line">Object user2 = context.getBean(<span class="string">"user"</span>);</span><br></pre></td></tr></table></figure><p></p><p>启动项目，观察输出:</p><p><img src="img/QQ截图20190129145157.png" alt="QQ截图20190129145157.png"></p><p>结果证实了我们的观点。</p><h2 id="条件注册组件"><a href="#条件注册组件" class="headerlink" title="条件注册组件"></a>条件注册组件</h2><h3 id="Conditional"><a href="#Conditional" class="headerlink" title="@Conditional"></a>@Conditional</h3><p>使用<code>@Conditional</code>注解我们可以指定组件注册的条件，即满足特定条件才将组件纳入到IOC容器中。</p><p>在使用该注解之前，我们需要创建一个类，实现<code>Condition</code>接口：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyCondition</span> <span class="keyword">implements</span> <span class="title">Condition</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">matches</span><span class="params">(ConditionContext context, AnnotatedTypeMetadata metadata)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>该接口包含一个<code>matches</code>方法，包含两个入参:</p><ol><li><p><code>ConditionContext</code>：上下文信息；</p></li><li><p><code>AnnotatedTypeMetadata</code>：注解信息。</p></li></ol><p>简单完善一下这个实现类:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyCondition</span> <span class="keyword">implements</span> <span class="title">Condition</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">matches</span><span class="params">(ConditionContext context, AnnotatedTypeMetadata metadata)</span> </span>&#123;</span><br><span class="line">        String osName = context.getEnvironment().getProperty(<span class="string">"os.name"</span>);</span><br><span class="line">        <span class="keyword">return</span> osName != <span class="keyword">null</span> &amp;&amp; osName.contains(<span class="string">"Windows"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>接着将这个条件添加到User Bean注册的地方：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="meta">@Conditional</span>(MyCondition.class)</span><br><span class="line"><span class="function"><span class="keyword">public</span> User <span class="title">user</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> User(<span class="string">"mrbird"</span>, <span class="number">18</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>在Windows环境下，User这个组件将被成功注册，如果是别的操作系统，这个组件将不会被注册到IOC容器中。</p><h3 id="Profile"><a href="#Profile" class="headerlink" title="@Profile"></a>@Profile</h3><p><code>@Profile</code>可以根据不同的环境变量来注册不同的组件，下面我们来学一下它的用法。</p><p>新建一个接口<code>CalculateService</code>：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">CalculateService</span> </span>&#123;</span><br><span class="line">    <span class="function">Integer <span class="title">sum</span><span class="params">(Integer... value)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>接着添加两个实现<code>Java7CalculateServiceImpl</code>和<code>Java8CalculateServiceImpl</code>:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="meta">@Profile</span>(<span class="string">"java7"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Java7CalculateServiceImpl</span> <span class="keyword">implements</span> <span class="title">CalculateService</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Integer <span class="title">sum</span><span class="params">(Integer... value)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"Java 7环境下执行"</span>);</span><br><span class="line">        <span class="keyword">int</span> result = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt;= value.length; i++) &#123;</span><br><span class="line">            result += i;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="meta">@Profile</span>(<span class="string">"java8"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Java8CalculateServiceImpl</span> <span class="keyword">implements</span> <span class="title">CalculateService</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Integer <span class="title">sum</span><span class="params">(Integer... value)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"Java 8环境下执行"</span>);</span><br><span class="line">        <span class="keyword">return</span> Arrays.stream(value).reduce(<span class="number">0</span>, Integer::sum);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>通过<code>@Profile</code>注解我们实现了：当环境变量包含<code>java7</code>的时候，<code>Java7CalculateServiceImpl</code>将会被注册到IOC容器中；当环境变量包含<code>java8</code>的时候，<code>Java8CalculateServiceImpl</code>将会被注册到IOC容器中。</p><p>测试一下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">ConfigurableApplicationContext context1 = <span class="keyword">new</span> SpringApplicationBuilder(DemoApplication.class)</span><br><span class="line">                .web(WebApplicationType.NONE)</span><br><span class="line">                .profiles(<span class="string">"java8"</span>)</span><br><span class="line">                .run(args);</span><br><span class="line"></span><br><span class="line">CalculateService service = context1.getBean(CalculateService.class);</span><br><span class="line">System.out.println(<span class="string">"求合结果： "</span> + service.sum(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>));</span><br></pre></td></tr></table></figure><p></p><p>启动项目，控制台输出如下：</p><p><img src="img/QQ截图20190221094636.png" alt="QQ截图20190221094636.png"></p><p><img src="img/QQ截图20190221094658.png" alt="QQ截图20190221094658.png"></p><p>如果将<code>.profiles(&quot;java8&quot;)</code>改为<code>.profiles(&quot;java7&quot;)</code>的话，控制台输出如下：</p><p><img src="img/QQ截图20190221094849.png" alt="QQ截图20190221094849.png"></p><p><img src="img/QQ截图20190221094910.png" alt="QQ截图20190221094910.png"></p><h2 id="导入组件"><a href="#导入组件" class="headerlink" title="导入组件"></a>导入组件</h2><h3 id="Import"><a href="#Import" class="headerlink" title="@Import"></a>@Import</h3><p>到目前为止，我们可以使用包扫描和<code>@Bean</code>来实现组件注册。除此之外，我们还可以使用<code>@Import</code>来快速地往IOC容器中添加组件。</p><p>创建一个新的类<code>Hello</code>：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Hello</span> </span>&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>然后在配置类中导入这个组件：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@Import</span>(&#123;Hello.class&#125;)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">WebConfig</span> </span>&#123;</span><br><span class="line">	...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>查看IOC容器中所有组件的名称：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">ApplicationContext context = <span class="keyword">new</span> AnnotationConfigApplicationContext(WebConfig.class);</span><br><span class="line">String[] beanNames = context.getBeanDefinitionNames();</span><br><span class="line">Arrays.stream(beanNames).forEach(System.out::println);</span><br></pre></td></tr></table></figure><p></p><p>启动项目，控制台输出:</p><p><img src="img/QQ截图20190129185401.png" alt="QQ截图20190129185401.png"></p><p>可看到，通过<code>@Import</code>我们可以快速地往IOC容器中添加组件，Id默认为全类名。</p><h3 id="ImportSelector"><a href="#ImportSelector" class="headerlink" title="ImportSelector"></a>ImportSelector</h3><p>通过<code>@Import</code>我们已经实现了组件的导入，如果需要一次性导入较多组件，我们可以使用<code>ImportSelector</code>来实现。</p><p>新增三个类<code>Apple</code>，<code>Banana</code>和<code>Watermelon</code>，代码略。</p><p>查看<code>ImportSelector</code>源码：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">ImportSelector</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Select and return the names of which class(es) should be imported based on</span></span><br><span class="line"><span class="comment">     * the &#123;<span class="doctag">@link</span> AnnotationMetadata&#125; of the importing @&#123;<span class="doctag">@link</span> Configuration&#125; class.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">     String[] selectImports(AnnotationMetadata importingClassMetadata);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p><code>ImportSelector</code>是一个接口，包含一个<code>selectImports</code>方法，方法返回类的全类名数组（即需要导入到IOC容器中组件的全类名数组），包含一个<code>AnnotationMetadata</code>类型入参，通过这个参数我们可以获取到使用<code>ImportSelector</code>的类的全部注解信息。</p><p>我们新建一个<code>ImportSelector</code>实现类<code>MyImportSelector</code>：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyImportSelector</span> <span class="keyword">implements</span> <span class="title">ImportSelector</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String[] selectImports(AnnotationMetadata importingClassMetadata) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> String[]&#123;</span><br><span class="line">                <span class="string">"cc.mrbird.demo.domain.Apple"</span>,</span><br><span class="line">                <span class="string">"cc.mrbird.demo.domain.Banana"</span>,</span><br><span class="line">                <span class="string">"cc.mrbird.demo.domain.Watermelon"</span></span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>上面方法返回了新增的三个类的全类名数组，接着我们在配置类的<code>@Import</code>注解上使用<code>MyImportSelector</code>来把这三个组件快速地导入到IOC容器中：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Import</span>(&#123;MyImportSelector.class&#125;)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">WebConfig</span> </span>&#123;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>查看容器中是否已经有这三个组件:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">ApplicationContext context = <span class="keyword">new</span> AnnotationConfigApplicationContext(WebConfig.class);</span><br><span class="line">String[] beanNames = context.getBeanDefinitionNames();</span><br><span class="line">Arrays.stream(beanNames).forEach(System.out::println);</span><br></pre></td></tr></table></figure><p></p><p>启动项目，观察控制台：</p><p><img src="img/QQ截图20190130091140.png" alt="QQ截图20190130091140.png"></p><p>组件已经成功导入。</p><h3 id="ImportBeanDefinitionRegistrar"><a href="#ImportBeanDefinitionRegistrar" class="headerlink" title="ImportBeanDefinitionRegistrar"></a>ImportBeanDefinitionRegistrar</h3><p>除了上面两种往IOC容器导入组件的方法外，我们还可以使用<code>ImportBeanDefinitionRegistrar</code>来手动往IOC容器导入组件。</p><p>查看其源码：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">ImportBeanDefinitionRegistrar</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">registerBeanDefinitions</span><span class="params">(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p><code>ImportBeanDefinitionRegistrar</code>是一个接口，包含一个<code>registerBeanDefinitions</code>方法，该方法包含两个入参：</p><ol><li><p><code>AnnotationMetadata</code>：可以通过它获取到类的注解信息；</p></li><li><p><code>BeanDefinitionRegistry</code>：Bean定义注册器，包含了一些和Bean有关的方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"> <span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">BeanDefinitionRegistry</span> <span class="keyword">extends</span> <span class="title">AliasRegistry</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">registerBeanDefinition</span><span class="params">(String var1, BeanDefinition var2)</span> <span class="keyword">throws</span> BeanDefinitionStoreException</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">removeBeanDefinition</span><span class="params">(String var1)</span> <span class="keyword">throws</span> NoSuchBeanDefinitionException</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function">BeanDefinition <span class="title">getBeanDefinition</span><span class="params">(String var1)</span> <span class="keyword">throws</span> NoSuchBeanDefinitionException</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">boolean</span> <span class="title">containsBeanDefinition</span><span class="params">(String var1)</span></span>;</span><br><span class="line"></span><br><span class="line">    String[] getBeanDefinitionNames();</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">getBeanDefinitionCount</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">boolean</span> <span class="title">isBeanNameInUse</span><span class="params">(String var1)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><p>这里我们需要借助<code>BeanDefinitionRegistry</code>的<code>registerBeanDefinition</code>方法来往IOC容器中注册Bean。该方法包含两个入参，第一个为需要注册的Bean名称（Id）,第二个参数为Bean的定义信息，它是一个接口，我们可以使用其实现类<code>RootBeanDefinition</code>来完成：</p><p><img src="img/QQ截图20190130094046.png" alt="QQ截图20190130094046.png"></p><p>为了演示<code>ImportBeanDefinitionRegistrar</code>的使用，我们先新增一个类，名称为<code>Strawberry</code>，代码略。</p><p>然后新增一个<code>ImportBeanDefinitionRegistrar</code>实现类<code>MyImportBeanDefinitionRegistrar</code>：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyImportBeanDefinitionRegistrar</span> <span class="keyword">implements</span> <span class="title">ImportBeanDefinitionRegistrar</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">registerBeanDefinitions</span><span class="params">(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">final</span> String beanName = <span class="string">"strawberry"</span>;</span><br><span class="line">        <span class="keyword">boolean</span> contain = registry.containsBeanDefinition(beanName);</span><br><span class="line">        <span class="keyword">if</span> (!contain) &#123;</span><br><span class="line">            RootBeanDefinition rootBeanDefinition = <span class="keyword">new</span> RootBeanDefinition(Strawberry.class);</span><br><span class="line">            registry.registerBeanDefinition(beanName, rootBeanDefinition);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>在上面的实现类中，我们先通过<code>BeanDefinitionRegistry</code>的<code>containsBeanDefinition</code>方法判断IOC容器中是否包含了名称为<code>strawberry</code>的组件，如果没有，则手动通过<code>BeanDefinitionRegistry</code>的<code>registerBeanDefinition</code>方法注册一个。</p><p>定义好<code>MyImportBeanDefinitionRegistrar</code>后，我们同样地在配置类的<code>@Import</code>中使用它：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@Import</span>(&#123;MyImportBeanDefinitionRegistrar.class&#125;)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">WebConfig</span> </span>&#123;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>查看容器中是否已经有这个组件:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">ApplicationContext context = <span class="keyword">new</span> AnnotationConfigApplicationContext(WebConfig.class);</span><br><span class="line">String[] beanNames = context.getBeanDefinitionNames();</span><br><span class="line">Arrays.stream(beanNames).forEach(System.out::println);</span><br></pre></td></tr></table></figure><p></p><p>启动项目，观察控制台：</p><p><img src="img/QQ截图20190130094912.png" alt="QQ截图20190130094912.png"></p><p>组件已经注册成功。</p><h2 id="使用FactoryBean注册组件"><a href="#使用FactoryBean注册组件" class="headerlink" title="使用FactoryBean注册组件"></a>使用FactoryBean注册组件</h2><p>Spring还提供了一个<code>FactoryBean</code>接口，我们可以通过实现该接口来注册组件，该接口包含了两个抽象方法和一个默认方法：</p><p><img src="img/QQ截图20190130103346.png" alt="QQ截图20190130103346.png"></p><p>为了演示<code>FactoryBean</code>的使用，我们新增一个<code>Cherry</code>类，代码略。</p><p>然后创建<code>FactoryBean</code>的实现类<code>CherryFactoryBean</code>:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CherryFactoryBean</span> <span class="keyword">implements</span> <span class="title">FactoryBean</span>&lt;<span class="title">Cherry</span>&gt; </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Cherry <span class="title">getObject</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> Cherry();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Class&lt;?&gt; getObjectType() &#123;</span><br><span class="line">        <span class="keyword">return</span> Cherry.class;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isSingleton</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>getObject</code>返回需要注册的组件对象，<code>getObjectType</code>返回需要注册的组件类型，<code>isSingleton</code>指明该组件是否为单例。如果为多例的话，每次从容器中获取该组件都会调用其<code>getObject</code>方法。</p><p>定义好<code>CherryFactoryBean</code>后，我们在配置类中注册这个类：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> CherryFactoryBean <span class="title">cherryFactoryBean</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> CherryFactoryBean();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>测试从容器中获取：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">ApplicationContext context = <span class="keyword">new</span> AnnotationConfigApplicationContext(WebConfig.class);</span><br><span class="line">Object cherry = context.getBean(<span class="string">"cherryFactoryBean"</span>);</span><br><span class="line">System.out.println(cherry.getClass());</span><br></pre></td></tr></table></figure><p></p><p>启动项目，观察控制台输出：</p><p><img src="img/QQ截图20190130103934.png" alt="QQ截图20190130103934.png"></p><p>可看到，虽然我们获取的是Id为<code>cherryFactoryBean</code>的组件，但其获取到的实际是<code>getObject</code>方法里返回的对象。</p><p>如果我们要获取<code>cherryFactoryBean</code>本身，则可以这样做：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Object cherryFactoryBean = context.getBean(<span class="string">"&amp;cherryFactoryBean"</span>);</span><br><span class="line">System.out.println(cherryFactoryBean.getClass());</span><br></pre></td></tr></table></figure><p></p><p>启动项目，观察控制台：</p><p><img src="img/QQ截图20190130104606.png" alt="QQ截图20190130104606.png"></p><p>为什么加上<code>&amp;</code>前缀就可以获取到相应的工厂类了呢，查看<code>BeanFactory</code>的源码会发现原因:</p><p><img src="img/QQ截图20190130104715.png" alt="QQ截图20190130104715.png"></p><blockquote><p>源码链接：<a href="https://github.com/wuyouzhuguli/SpringAll/tree/master/50.Spring-Regist-Bean" target="_blank" rel="noopener">https://github.com/wuyouzhuguli/SpringAll/tree/master/50.Spring-Regist-Bean</a></p></blockquote><script>$(".post-body a").not(".thispage").addClass("ignore-href").attr("target","_blank")</script></div><div></div><div><div style="padding:10px 0;margin:20px auto;width:90%;text-align:center;color:#878787"><div>请作者喝瓶肥宅水~</div><button id="rewardButton" style="margin-top:10px" disable="enable" onclick='var e=document.getElementById("QR");"none"===e.style.display?e.style.display="block":e.style.display="none"'><span style="height:46px;width:46px;line-height:46px;border-radius:50%;color:#fe5f55;font-weight:600;background:#ffd5be;border:none;box-shadow:0 4px 8px 0 rgba(255,213,190,.4)">￥</span></button><div id="QR" style="display:none"><div id="wechat" style="display:inline-block"><img id="wechat_qr" src="/img/wechat_pay.png" alt="MrBird WeChat Pay"></div><div id="alipay" style="display:inline-block"><img id="alipay_qr" src="/img/ali_pay.png" alt="MrBird Alipay"></div></div></div><style>#QR img{width:auto;margin:.8em 1em 0 1em}</style></div><div><ul class="post-copyright"><li class="post-copyright-author"><strong>本文作者：</strong> MrBird</li><li class="post-copyright-link"><strong>本文链接：</strong> <a href="http://mrbird.cc/Spring-Bean-Regist.html" title="深入学习Spring组件注册">http://mrbird.cc/Spring-Bean-Regist.html</a></li><li class="post-copyright-license"><strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" rel="external nofollow" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明出处！</li></ul></div><footer class="post-footer"><div class="post-tags" style="margin-bottom:1.3rem"><a href="/tags/Spring/" rel="tag"># Spring</a></div><div class="post-nav"><div class="post-nav-next post-nav-item"><a href="/Dubbo-High-Availability.html" rel="next" title="Dubbo的高可用"><i class="fa fa-chevron-left"></i> Dubbo的高可用</a></div><span class="post-nav-divider"></span><div class="post-nav-prev post-nav-item"><a href="/Spring-Bean-Lifecycle.html" rel="prev" title="深入学习Spring Bean生命周期">深入学习Spring Bean生命周期 <i class="fa fa-chevron-right"></i></a></div></div></footer></article><hr><div id="container"></div><div class="post-spread"><div id="comment-div"></div><style>.valine .vlist{margin-bottom:3rem}.valine .vwrap .vcontrol .col.col-60{text-align:left}.valine .vlist .vcard .vhead,.valine .vlist .vcard section .vfooter{text-align:left}.valine .vlist .vcard section{padding-bottom:.5rem!important}.vname{color:#42b983!important}.valine .vinfo .col{text-align:left;margin-left:-27rem}div#comment-div{margin-bottom:-8rem}.valine .vlist .vcard .vcontent .code,.valine .vlist .vcard .vcontent code,.valine .vlist .vcard .vcontent pre{background:#fbfbfb}.valine .vlist .vcard .vcontent a{color:#42b983}.valine .vlist .vcard .vimg{border-radius:3px}.valine .vbtn{border-radius:2px;padding:.3rem 1.25rem}.valine .vbtn:active,.valine .vbtn:hover{color:#42b983;border-color:#42b983;background-color:#fff}.valine .vwrap .vheader .vinput:focus{border-bottom-color:#42b983}.valine .vlist .vcard .vcontent.expand:before{background:-webkit-gradient(linear,left top,left bottom,from(hsla(0,0%,100%,0)),to(hsla(0,0%,100%,.2)));background:linear-gradient(180deg,hsla(0,0%,100%,0),hsla(0,0%,100%,.2))}.valine .vlist .vcard .vcontent.expand:after{content:"点击展开";font-size:.4rem;text-align:right;left:-1rem;background:hsla(0,0%,100%,.2)}.valine .vlist .vcard section .vfooter .vat{color:#b3b3b3}.valine .vlist .vcard section .vfooter .vat:hover{color:#42b983}.vcontent img{margin:0}.valine .info{display:none}</style><script type="text/javascript" src="/js/av.min.js"></script><script type="text/javascript" src="/js/Valine.min.js"></script><script type="text/javascript" src="/js/activate-power-mode.js"></script><script>POWERMODE.colorful=!0,POWERMODE.shake=!1,document.body.addEventListener("input",POWERMODE),new Valine({el:"#comment-div",notify:!1,verify:!0,appId:"SMcDFP1bN1jgb9Lo8JmtICHm-gzGzoHsz",appKey:"dH4nrUrt3V5XiJiI6Qyejnbi",placeholder:"",path:window.location.pathname,avatar:"monsterid",guest_info:["nick","mail","link"]});var chicken='<a href="#"><img src="https://image.uisdc.com/wp-content/uploads/2018/06/uisdc-chat-chicken.gif" class="checken"></a>';$("#comment-div").prepend(chicken)</script></div></div><script>var $bqinline=$("img[alt='bq-inline']");$bqinline.css({width:"2.3rem",height:"2.3rem",display:"inline","vertical-align":"text-bottom"})</script></div><div class="comments" id="comments"></div></div><aside id="sidebar" class="sidebar" onselectstart="return!1"><div class="sidebar-inner"><ul class="sidebar-nav motion-element"><li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">Contents</li><li class="sidebar-nav-overview" data-target="site-overview">Site Preview</li></ul><section class="site-overview sidebar-panel"><div class="sidebar-sticky sticky"><div itemscope itemtype="http://schema.org/Person"><div class="author__avatar"><img src="/images/blogImage.jpg" class="author__avatar" alt="MrBird" itemprop="image"></div><div class="author__content"><h3 class="author__name" itemprop="name">MrBird's Blog</h3><p class="author__bio" itemprop="description">A simple blog, code repository, just keep blogging</p></div><div class="author__urls-wrapper"><button class="btn btn--inverse">Follow</button><ul class="author__urls social-icons"><li><a href="http://map.baidu.com/?newmap=1&s=s%26wd%3D%E7%A6%8F%E5%B7%9E%E5%B8%82%26c%3D300&from=alamap&tpl=mapcity" target="_blank" itemprop="url" class="ignore-href"><i class="fa fa-fw fa-map-marker" aria-hidden="true"></i>&nbsp;&nbsp;FuZhou,CN</a></li><li><a href="https://love.mrbird.cc" target="_blank" itemprop="url" class="ignore-href"><i class="fa fa-fw fa-diamond" aria-hidden="true"></i>&nbsp;&nbsp;Love</a></li><li><a href="https://cloud.mrbird.cn" target="_blank" itemprop="url" class="ignore-href"><i class="fa fa-fw fa-chain" aria-hidden="true"></i>&nbsp;&nbsp;FEBS</a></li><li><a href="/atom.xml" target="_blank" itemprop="url" class="ignore-href"><i class="fa fa-fw fa-rss" aria-hidden="true"></i>&nbsp;&nbsp;RSS</a></li><li><a href="https://gitee.com/mrbirdd" target="_blank" itemprop="sameAs" class="ignore-href"><i class="fa fa-fw fa-codepen" aria-hidden="true"></i>&nbsp;&nbsp;Gitee</a></li><li><a href="https://github.com/wuyouzhuguli" target="_blank" itemprop="sameAs" class="ignore-href"><i class="fa fa-fw fa-github-alt" aria-hidden="true"></i>&nbsp;&nbsp;GitHub</a></li><li><a href="javascript:;" class="popup-trigger"><i class="fa fa-fw fa-search" aria-hidden="true"></i>&nbsp;&nbsp;Search</a></li></ul></div></div></div><script>var $urls=$(".author__urls").find("a");$urls.each(function(){var o=$(this);o.mouseenter(function(){o.css({color:"#414547"})}),o.mouseleave(function(){o.css({color:""})})})</script></section><section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active"><div class="post-toc"><div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#通过-Bean注册组件"><span class="nav-number">1.</span> <span class="nav-text">通过@Bean注册组件</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#使用-ComponentScan扫描"><span class="nav-number">2.</span> <span class="nav-text">使用@ComponentScan扫描</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#指定扫描策略"><span class="nav-number">2.1.</span> <span class="nav-text">指定扫描策略</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#多扫描策略配置"><span class="nav-number">2.2.</span> <span class="nav-text">多扫描策略配置</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#自定义扫描策略"><span class="nav-number">2.3.</span> <span class="nav-text">自定义扫描策略</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#组件作用域-Scope"><span class="nav-number">3.</span> <span class="nav-text">组件作用域@Scope</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#懒加载-Lazy"><span class="nav-number">4.</span> <span class="nav-text">懒加载@Lazy</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#条件注册组件"><span class="nav-number">5.</span> <span class="nav-text">条件注册组件</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Conditional"><span class="nav-number">5.1.</span> <span class="nav-text">@Conditional</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Profile"><span class="nav-number">5.2.</span> <span class="nav-text">@Profile</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#导入组件"><span class="nav-number">6.</span> <span class="nav-text">导入组件</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Import"><span class="nav-number">6.1.</span> <span class="nav-text">@Import</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#ImportSelector"><span class="nav-number">6.2.</span> <span class="nav-text">ImportSelector</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#ImportBeanDefinitionRegistrar"><span class="nav-number">6.3.</span> <span class="nav-text">ImportBeanDefinitionRegistrar</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#使用FactoryBean注册组件"><span class="nav-number">7.</span> <span class="nav-text">使用FactoryBean注册组件</span></a></li></ol></div></div></section></div></aside></div></main><footer id="footer" class="footer" onselectstart="return!1"><div class="footer-inner"><div class="copyright">&copy; 2016 - <span itemprop="copyrightYear">2019</span>&nbsp;&nbsp; <span class="author" itemprop="copyrightHolder">MrBird</span>&nbsp;&nbsp;|<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>&nbsp;&nbsp;UV&nbsp;<span class="busuanzi-value" id="busuanzi_value_site_uv" style="cursor:pointer" title="统计开始时间：2019年7月5日"></span> &nbsp;&nbsp;PV&nbsp;<span class="busuanzi-value" id="busuanzi_value_site_pv" style="cursor:pointer" title="统计开始时间：2019年7月5日"></span></div></div></footer><div class="back-to-top"><span style="font-family:'Source Sans Pro','Helvetica Neue',Arial,sans-serif;font-size:1.2em;font-weight:600">TOP</span></div></div><script type="text/javascript">"[object Function]"!==Object.prototype.toString.call(window.Promise)&&(window.Promise=null)</script><script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script><script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script><script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script><script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script><script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script><script type="text/javascript" src="/js/src/utils.js?v=5.1.1"></script><script type="text/javascript" src="/js/src/motion.js?v=5.1.1"></script><script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.1"></script><script type="text/javascript" src="/js/src/post-details.js?v=5.1.1"></script><script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.1"></script><script type="text/javascript">function proceedsearch(){$("body").append('<div class="search-popup-overlay local-search-pop-overlay"></div>').css("overflow","hidden"),$(".search-popup-overlay").click(onPopupClose),$(".popup").toggle();var t=$("#local-search-input");t.attr("autocapitalize","none"),t.attr("autocorrect","off"),t.focus()}var isfetched=!1,isXml=!0,search_path="search.xml";0===search_path.length?search_path="search.xml":search_path.endsWith("json")&&(isXml=!1);var path="/"+search_path,onPopupClose=function(t){$(".popup").hide(),$("#local-search-input").val(""),$(".search-result-list").remove(),$("#no-result").remove(),$(".local-search-pop-overlay").remove(),$("body").css("overflow","")},searchFunc=function(t,e,o){"use strict";$("body").append('<div class="search-popup-overlay local-search-pop-overlay"><div id="search-loading-icon"><i class="fa fa-spinner fa-pulse fa-2x fa-fw"></i></div></div>').css("overflow","hidden"),$("#search-loading-icon").css("margin","20% auto 0 auto").css("text-align","center"),$.ajax({url:t,dataType:isXml?"xml":"json",async:!0,success:function(t){isfetched=!0,$(".popup").detach().appendTo(".header-inner");var n=isXml?$("entry",t).map(function(){return{title:$("title",this).text(),content:$("content",this).text(),url:$("url",this).text()}}).get():t,r=document.getElementById(e),s=document.getElementById(o),a=function(){var t=r.value.trim().toLowerCase(),e=t.split(/[\s\-]+/);e.length>1&&e.push(t);var o=[];if(t.length>0&&n.forEach(function(n){function r(e,o,n,r){for(var s=r[r.length-1],a=s.position,i=s.word,l=[],h=0;a+i.length<=n&&0!=r.length;){i===t&&h++,l.push({position:a,length:i.length});var p=a+i.length;for(r.pop();0!=r.length&&(s=r[r.length-1],a=s.position,i=s.word,p>a);)r.pop()}return c+=h,{hits:l,start:o,end:n,searchTextCount:h}}function s(t,e){var o="",n=e.start;return e.hits.forEach(function(e){o+=t.substring(n,e.position);var r=e.position+e.length;o+='<b class="search-keyword">'+t.substring(e.position,r)+"</b>",n=r}),o+=t.substring(n,e.end)}var a=!1,i=0,c=0,l=n.title.trim(),h=l.toLowerCase(),p=n.content.trim().replace(/<[^>]+>/g,""),u=p.toLowerCase(),f=decodeURIComponent(n.url),d=[],g=[];if(""!=l&&(e.forEach(function(t){function e(t,e,o){var n=t.length;if(0===n)return[];var r=0,s=[],a=[];for(o||(e=e.toLowerCase(),t=t.toLowerCase());(s=e.indexOf(t,r))>-1;)a.push({position:s,word:t}),r=s+n;return a}d=d.concat(e(t,h,!1)),g=g.concat(e(t,u,!1))}),(d.length>0||g.length>0)&&(a=!0,i=d.length+g.length)),a){[d,g].forEach(function(t){t.sort(function(t,e){return e.position!==t.position?e.position-t.position:t.word.length-e.word.length})});var v=[];0!=d.length&&v.push(r(l,0,l.length,d));for(var C=[];0!=g.length;){var $=g[g.length-1],m=$.position,x=$.word,w=m-20,y=m+80;w<0&&(w=0),y<m+x.length&&(y=m+x.length),y>p.length&&(y=p.length),C.push(r(p,w,y,g))}C.sort(function(t,e){return t.searchTextCount!==e.searchTextCount?e.searchTextCount-t.searchTextCount:t.hits.length!==e.hits.length?e.hits.length-t.hits.length:t.start-e.start});var T=parseInt("1");T>=0&&(C=C.slice(0,T));var b="";b+=0!=v.length?"<li><a href='"+f+"' class='search-result-title'>"+s(l,v[0])+"</a>":"<li><a href='"+f+"' class='search-result-title'>"+l+"</a>",C.forEach(function(t){b+="<a href='"+f+'\'><p class="search-result">'+s(p,t)+"...</p></a>"}),b+="</li>",o.push({item:b,searchTextCount:c,hitCount:i,id:o.length})}}),1===e.length&&""===e[0])s.innerHTML='<div id="no-result"><i class="fa fa-search fa-5x" /></div>';else if(0===o.length)s.innerHTML='<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>';else{o.sort(function(t,e){return t.searchTextCount!==e.searchTextCount?e.searchTextCount-t.searchTextCount:t.hitCount!==e.hitCount?e.hitCount-t.hitCount:e.id-t.id});var a='<ul class="search-result-list">';o.forEach(function(t){a+=t.item}),a+="</ul>",s.innerHTML=a}};r.addEventListener("input",a),$(".local-search-pop-overlay").remove(),$("body").css("overflow",""),proceedsearch()}})};$(".popup-trigger").click(function(t){t.stopPropagation(),isfetched===!1?searchFunc(path,"local-search-input","local-search-result"):proceedsearch()}),$(".popup-btn-close").click(onPopupClose),$(".popup").click(function(t){t.stopPropagation()}),$(document).on("keyup",function(t){var e=27===t.which&&$(".search-popup").is(":visible");e&&onPopupClose()})</script></body><script>$(function(){$("a").not(".nav-link,.cloud-tie-join-count,.ignore-href,.jstree-anchor").addClass("animsition-link")});var burst1=new mojs.Burst({left:0,top:0,radius:{5:40},children:{shape:"circle",fill:["red","cyan","orange"],fillOpacity:.8,radiusX:3.5,radiusY:3.5}});document.addEventListener("click",function(a){null==a.target.href&&"footer"!=a.target.className&&"copyright"!=a.target.className&&"author__urls social-icons"!=a.target.className&&"author__avatar"!=a.target.className&&"sidebar sidebar-active"!=a.target.className&&burst1.tune({x:a.pageX,y:a.pageY}).generate().replay()})</script><script type="text/javascript" src="/js/message.js"></script></html><!-- rebuild by neat -->